/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2017.07.18                                          *
 * Description:      the structure to save fluent mesh information       *
 *                                                                       *
 *************************************************************************/
#ifndef FreeMAPs_include_COMMON_mesh_FluentMesh_h
#define FreeMAPs_include_COMMON_mesh_FluentMesh_h

#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include<set>

class vtkUnstructuredGrid;

namespace FreeMAPs
{

/**
 * @brief the flunet's *.msh file parser
 */
class FluentMesh
{
public:

    /** Nodal data structure */
    struct Node_t
    {
        /** node index, (sequential, starts at 1) */
        int    index;

        /** X-coordinate of node  */
        double x;

        /** Y-coordinate of node  */
        double y;

        /** Z-coordinate of node  */
        double z;

        /** which region contains this node */
        int region;
    };

    /**
     * element-type  face type  nodes/face
     *     0          mixed
     *     2         linear         2
     *     3        triangular      3
     *     4       quadrilateral    4
     */
    struct Face_t
    {
        /** face index, (sequential, starts at 1) */
        int index;
        
        int region;

        /** face type id */
        int type;

        /** face nodes list, in right-hand rule */
        std::vector<int> nodes;

        /** cell id on its right */
        int cell_right;

        /** cell id on its left */
        int cell_left;
    };

    /**
     * the '<' operator used in std::map
     */
    struct lt_face
    {
        bool operator()(const Face_t &_f1, const Face_t &_f2) const
        {
            if(_f1.nodes.size() != _f2.nodes.size()) return _f1.nodes.size() < _f2.nodes.size();

            std::vector<int> nodes1 = _f1.nodes;
            std::vector<int> nodes2 = _f2.nodes;

            std::sort(nodes1.begin(), nodes1.end());
            std::sort(nodes2.begin(), nodes2.end());

            for(size_t i=0; i<nodes1.size(); i++)
            {
                if( nodes1[i] != nodes2[1] ) return nodes1[i] < nodes2[i];
            }
            return false;
        }
    };

    /**
     * element-type         description        nodes/cell    faces/cell
     *    0                   mixed
     *    1                   triangular           3            3
     *    2                   tetrahedral          4            4
     *    3                   quadrilateral        4            4
     *    4                   hexahedral           8            6
     *    5                   pyramid              5            5
     *    6                   wedge                6            5
     */
    struct Cell_t
    {
        /** cell index, (sequential, starts at 1) */
        int index;

        /** region index */
        int region;

        /** cell type id */
        int type;

        /** cell nodes list */
        std::vector<int> nodes;

        /** cell faces list */
        std::vector<int> faces;
    };


    /** Region information structure */
    struct Region_t
    {
        /** region index */
        int  index;

        /** the node num of this region */
        //int  node_num;

        /** the cell num of this region */
        //int  cell_num;

        /** region type */
        std::string  type;

        /** name of the region */
        std::string name;
    };

private:

    /*
     * GrammarTree
     */
    struct GrammarTreeNode
    {
        GrammarTreeNode():parent(0) {}
        ~GrammarTreeNode() {for(size_t c=0; c<child.size(); c++) delete child[c];}

        std::vector<std::string> content;
        GrammarTreeNode * parent;
        std::vector<GrammarTreeNode *> child;
    };

    struct Vertex2D
    {
        int index;
        Vertex2D * next;
    };

public:

    FluentMesh(
        const std::string& file);

    ~FluentMesh();

public:

    inline const std::vector<Node_t>&
    get_mesh_nodes() const;

    inline const std::vector<Cell_t>&
    get_mesh_cells() const;

    inline const std::vector<Face_t>&
    get_mesh_faces() const;

    inline const std::map<int, Region_t>&
    get_mesh_regions() const;

    inline const int
    get_dim() const;

    inline const int
    get_mesh_node_num() const;

    inline const std::vector<std::string>
    get_region_names() const;

public:

    void
    clear();

    /**
     * read the flunet msh file into internal meta data
     */
    int
    read();

    /**
     * debug purpose
     */
    void
    export_vtk(
        const std::string& vtk_file_name) const;

private:

    /*
     * lexer
     */

    /*
     * read file into d_content
     */
    int
    read_fluent_mesh_file();

    /*
     * scan the mesh d_content, do preprocess
     */
    int lex_pre_process();

    /*
     * split d_content into token
     */
    int
    do_lex();

    /*
     * parser, process tokens to get mesh nodes, faces, cells regions, etc
     */

    /*
     * call this function to do the parse process
     */
    int
    do_parse();

    /*
    * key 2
    */
    void
    process_mesh_dimension(
        const GrammarTreeNode* block);

    /*
     * key 10
     */
    void
    process_mesh_nodes(
        const GrammarTreeNode* block);

    /*
     * key 12
     */
    void
    process_mesh_cells(
        const GrammarTreeNode* block);

    /*
     * key 13
     */
    void
    process_mesh_faces(
        const GrammarTreeNode* block);

    /*
     * key 45
     */
    void
    process_mesh_regions(
        const GrammarTreeNode* block);

    /*
     * built mesh information
     */
    int
    post_process();

    void
    process_triangle (Cell_t& cell);

    void
    process_tetrahedron (Cell_t& cell);

    void
    process_quadrangle (Cell_t& cell);

    void
    process_hexahedra (Cell_t& cell);

    void
    process_pyramid (Cell_t& cell);

    void
    process_wedge (Cell_t& cell);

    static void
    replace_all (
        std::string& str,
        const std::string& from,
        const std::string& to);

    static int
    hex_string_to_int (
        const std::string& str);

    static int dec_string_to_int(
        const std::string& str);

    static double string_to_double(
        const std::string& str);

    static void unsorted_remove_duplicates(
        std::vector<int>& numbers);

private:
    bool
    is_region(
            const std::string& region_type);
private:

    const std::string d_fluent_mesh_file_name;

    /*
     * fluent mesh content
     */
    std::string d_content;

    /*
     * split fluent mesh content into tokens
     */
    std::vector<std::string> d_tokens;

    int d_mesh_dimension;

    // all the mesh nodes
    int d_mesh_node_num;
    std::vector<Node_t> d_mesh_nodes;

    // all the mesh cells
    int d_mesh_cell_num;
    std::vector<Cell_t> d_mesh_cells;

    // all the mesh faces
    int d_mesh_face_num;
    std::vector<Face_t> d_mesh_faces;

    /*
     * mesh region/boundary
     * map<region_index, region>
     */    
    std::map<int, Region_t> d_mesh_regions;

    std::vector<std::string> d_region_names;

};

} //namespace FreeMAPs

#include "COMMON/mesh/FluentMesh.inl"

#endif
